package com.ddpt.permission.service.login;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.lh.center.common.constant.GlobalConstant;
import com.lh.center.common.constant.RedisPrefix;
import com.lh.center.common.dto.LoginByPhoneDto;
import com.lh.center.common.exception.CommonException;
import com.lh.center.common.exception.ErrorEnum;
import com.lh.center.common.web.session.SessionOperation;
import com.lh.center.permission.api.entity.BackAdminUser;
import com.lh.center.permission.api.entity.Permission;
import com.lh.center.permission.api.session.BackstageSessionUser;
import com.lh.center.permission.api.session.SessionPermission;
import com.lh.center.permission.service.BackAdminUserService;
import com.lh.center.permission.service.LoginService;
import com.lh.center.permission.service.PermissionService;
import com.lh.center.permission.service.RoleService;
import com.lh.center.permission.util.ImageCodeUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service("backstageLoginService")
public class BackstageLoginServiceImpl implements LoginService<BackstageSessionUser> {

    @Value("${admin.timeout.backstage:30}")
    private Integer loginTimeout;

    @Resource
    private ImageCodeUtil imageCodeUtil;

    @Resource
    private BackAdminUserService backAdminUserService;

    @Resource
    private RoleService roleService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public BackstageSessionUser loginByPassword(String account, String password, String imgCode, String codeId) {
        if (StringUtils.isAnyBlank(account, password, imgCode, codeId)) {
            throw new CommonException(ErrorEnum.CRITICAL_PARAM_DEFICIENCY);
        }
        imageCodeUtil.verifyCode(codeId, imgCode);
        return loginByPassword(account, password);
    }

    @Override
    public BackstageSessionUser loginByPassword(String account, String password) {
        BackAdminUser user = backAdminUserService.getAvailableUserByUsernameAndPassword(account,password);
        if(null == user) {
            throw new CommonException(ErrorEnum.USERNAME_OR_PASSWORD_ERROR);
        }
        List<SessionPermission> permissionList = roleService.queryBackAdminPermission(user.getType(),user.getRoleId());
        BackstageSessionUser sessionUser = new BackstageSessionUser();
        sessionUser.setUserId(user.getId());
        sessionUser.setUsername(user.getUsername());
        sessionUser.setShowName(user.getShowName());
        sessionUser.setAdminType(user.getType());
        sessionUser.setAccount(user.getUsername());
        sessionUser.setApiPaths(new HashSet<>(Collections.singleton("/**")));
        if (!user.getType().equals(GlobalConstant.ZERO)) {
            //非超管，确定具体接口权限
            Set<String> finalApiPaths = new HashSet<>();
            permissionList.forEach(p -> {
                if(StringUtils.isNotBlank(p.getApiPath())){
                    finalApiPaths.addAll(Stream.of(p.getApiPath().split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toList()));
                }
            });
            finalApiPaths.addAll(Arrays.asList(
                    "/api/permission/login/keepLive",
                    "/api/merchant/serviceModule/queryList",
                    "/api/merchant/press/pressPage",
                    "/api/merchant/merchantIns/queryTree"
            ));
            sessionUser.setApiPaths(finalApiPaths);
        }
        String token = UUID.randomUUID().toString();
        sessionUser.setToken(token);
        //是否开启加解密
        if(SessionOperation.getConfiguration().isEnableSecret()){
            sessionUser.setSecret(RandomUtil.randomString(16));
            sessionUser.setSecretEnabled(true);
        }
        stringRedisTemplate.opsForValue().set(RedisPrefix.BACK_ADMIN_USER_TOKEN + token, JSONUtil.toJsonStr(sessionUser), loginTimeout, TimeUnit.MINUTES);
        //这个权限树不需要进行缓存
        sessionUser.setPermissionList(buildLayerPermission(permissionList));
        return sessionUser;
    }

    //权限组建成树状结构
    public List<SessionPermission> buildLayerPermission(List<SessionPermission> permissionList) {
        List<SessionPermission> targetList = new ArrayList<>();
        if (CollUtil.isEmpty(permissionList)) {
            return targetList;
        }
        Map<Long, List<SessionPermission>> permiMap = permissionList.stream().collect(Collectors.groupingBy(SessionPermission::getParentId));
        for (SessionPermission permission : permiMap.get(0L)) {
            targetList.add(permission);
            buildLayerPermission(permission, permiMap);
        }
        //子权限组装父权限
        /**
         * 1.递归组装到parentId = 0
         * 2.判断targetList是否已经有该权限树，如没有则加入到targetList
         * 3.递归查询children的不同，将不同的children加入到targetList里
         */
        List<Long> perList = permiMap.keySet().stream().filter(e -> !e.equals(0L)).collect(Collectors.toList());
        for (int i = 0; i < perList.size(); i++) {
            buildParentPermission(perList, permiMap, targetList);
        }

        return targetList;
    }

    private void buildParentPermission(List<Long> perList, Map<Long, List<SessionPermission>> permiMap, List<SessionPermission> targetList) {
        for (int i = 0; i < perList.size(); i++) {
            //获取子权限
            List<SessionPermission> list = permiMap.get(perList.get(i));
            //父节点
            Permission parent = permissionService.getById(list.get(0).getParentId());
            assemParentPermission(list, parent, targetList);
        }
    }

    private void assemParentPermission(List<SessionPermission> list, Permission parent, List<SessionPermission> targetList) {
        SessionPermission permission = new SessionPermission();
        BeanUtils.copyProperties(parent, permission);
        permission.setChildren(list);
        if (permission.getParentId().equals(0L)) {
            //需要组装父权限的子权限id各是一个列表，需要判断之前是否已经组装，未组装直接加入，组装过则递归查询children的不同
            if (targetList.stream().anyMatch(e -> e.getId().equals(permission.getId()))) {
                //递归搜索子节点的差异 二级权限已经全部组装，走到这至少是三级权限，二级权限只会是一个
                List<SessionPermission> targetChildren = targetList.stream().filter(e -> e.getId().equals(permission.getId())).findFirst().get().getChildren();
                assemChildrenPermission(targetChildren, permission.getChildren().get(0));
            } else {
                targetList.add(permission);
            }
        } else {
            //父节点
            Permission permissionParent = permissionService.getById(permission.getParentId());
            assemParentPermission(Arrays.asList(permission), permissionParent, targetList);
        }
    }

    private void assemChildrenPermission(List<SessionPermission> targetChildren, SessionPermission permissionChildren) {
        //children相同递归，不.同则加入到targetList中,id相同只会有一个
        List<SessionPermission> flag = targetChildren.stream().filter(e -> e.getId().equals(permissionChildren.getId())).collect(Collectors.toList());
        //没有相同session
        if (CollectionUtils.isEmpty(flag)) {
            targetChildren.add(permissionChildren);
        } else {
            //按父节点分组，不同只会出现在父节点处，所以一定只有一个children
            if (CollectionUtils.isNotEmpty(permissionChildren.getChildren())) {
                assemChildrenPermission(flag.get(0).getChildren(), permissionChildren.getChildren().get(0));
            }
        }
    }

    private void buildLayerPermission(SessionPermission parent, Map<Long, List<SessionPermission>> permiMap) {
        List<SessionPermission> subList = permiMap.get(parent.getId());
        if (CollUtil.isNotEmpty(subList)) {
            parent.setChildren(subList);
            for (SessionPermission child : subList) {
                buildLayerPermission(child, permiMap);
            }
        }
    }

    @Override
    public BackstageSessionUser loginByPhone(LoginByPhoneDto dto) {
        return null;
    }

    @Override
    public void signOut(String token) {
        if (StringUtils.isNotBlank(token)) {
            stringRedisTemplate.delete(RedisPrefix.BACK_ADMIN_USER_TOKEN + token);
        }
    }
}
